package com.socket.client.manager;

import cn.hutool.json.JSONUtil;
import com.socket.client.feign.ChatRecordApi;
import com.socket.client.feign.SysUserApi;
import com.socket.client.feign.param.RecordParam;
import com.socket.client.feign.param.UserParam;
import com.socket.client.model.base.BaseUser;
import com.socket.client.model.chat.ChatMessage;
import com.socket.client.model.chat.ChatUser;
import com.socket.client.model.chat.SysUser;
import com.socket.client.model.chat.UserExt;
import com.socket.client.model.vo.SysLogVo;
import com.socket.core.constant.Topics;
import com.socket.core.enums.LogType;
import com.socket.core.manage.SocketRedisManager;
import com.socket.core.manage.TokenUserManager;
import com.socket.core.model.TokenUser;
import com.socket.core.model.command.BaseCommand;
import com.socket.core.model.command.enmus.Command;
import com.socket.core.util.Enums;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.stereotype.Component;
import org.yeauty.pojo.Session;

import java.util.List;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * Socket用户管理器
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class UserManager extends ConcurrentHashMap<String, ChatUser> {
    private final RocketMQTemplate messageQueue;
    private final TokenUserManager tokenUserManager;
    private final SocketRedisManager redisManager;
    private final ChatRecordApi chatRecordApi;
    private final SysUserApi sysUserApi;

    /**
     * 加入用户
     *
     * @param session Scoket会话
     * @param ext     用户信息扩展
     * @return 完整Secket用户
     */
    public ChatUser join(Session session, UserExt ext) {
        // 通过令牌查找认证用户
        String token = ext.getToken();
        TokenUser tokenUser = tokenUserManager.getTokenUser(token);
        if (tokenUser == null) {
            return null;
        }
        // 查找Socket用户
        ChatUser user = getUser(tokenUser.getUid());
        // 检查重复登录
        if (user.isOnline() && user.differentToken(token)) {
            user.logout("您的账号已在别处登录");
            tokenUserManager.removeUser(user.getExt().getToken());
        }
        // 写入聊天室
        user.login(session, ext, tokenUserManager::getTokenUser);
        // 检查登录限制（会话缓存检查）
        long time = redisManager.getLockTime(user.getGuid());
        if (time > 0) {
            user.logout("您已被管理员限制登陆{}", time);
            return null;
        }
        // 记录登录信息
        saveLog(user, LogType.LOGIN);
        return user;
    }

    /**
     * 通过uid获取用户（不存在时通过数据库获取）
     *
     * @param uid 用户uid
     * @return {@link ChatUser}
     */
    public ChatUser getUser(String uid) {
        return Optional.ofNullable(uid).map(this::get).orElseGet(() -> {
            UserParam param = new UserParam();
            param.setGuid(uid);
            param.setHideProvince(true);
            SysUser data = sysUserApi.detail(param).getData();
            // 写入缓存
            Optional.ofNullable(data).ifPresent(e -> {
                log.debug("同步缓存用户：{}", uid);
                put(e.getGuid(), new ChatUser(e));
            });
            return get(uid);
        });
    }

    /**
     * 退出聊天室
     */
    public void exit(ChatUser target, Session session) {
        target.logout(session);
        // 发送退出通知
        if (!target.isOnline()) {
            this.sendAll(Command.EXIT, target);
        }
        // 保存日志
        saveLog(target, LogType.LOGOUT);
    }

    /**
     * 推送用户日志
     */
    private void saveLog(ChatUser user, LogType type) {
        SysLogVo vo = new SysLogVo();
        vo.setGuid(user.getGuid());
        vo.setPlatform(user.getPlatform());
        vo.setClientIp(user.getExt().getClientIp());
        vo.setType(Enums.key(type));
        messageQueue.syncSend(Topics.LOGGER, JSONUtil.toJsonStr(vo));
    }

    /**
     * @see #sendAll(String, BaseCommand, Object)
     */
    public void sendAll(String content, BaseCommand<?> command) {
        this.sendAll(content, command, null);
    }

    /**
     * 向所有用户发送系统消息
     *
     * @param content 消息内容
     * @param command 消息类型
     * @param data    附加用户信息
     */
    public void sendAll(String content, BaseCommand<?> command, Object data) {
        this.values().forEach(user -> user.send(content, command, data));
    }

    /**
     * @see #sendAll(String, BaseCommand, Object)
     */
    public void sendAll(BaseCommand<?> command, Object data) {
        this.sendAll(null, command, data);
    }

    /**
     * 更新发起人的消息为已读
     *
     * @param self   发起人
     * @param target 目标
     * @param audio  是否包括语音消息
     */
    public void readAllMessage(String self, String target, boolean audio) {
        RecordParam param = new RecordParam();
        param.setGuid(self);
        param.setTarget(target);
        param.setAudio(audio);
        chatRecordApi.readAllMessage(param);
        redisManager.setUnreadCount(self, target, 0);
    }

    /**
     * 获取指定用户的未读消息数量
     *
     * @param self   自己
     * @param target 目标
     * @return 未读消息数
     */
    public int getUnreads(String self, String target) {
        return redisManager.getUnreadCount(self, target);
    }

    /**
     * 保存聊天记录
     *
     * @param message 聊天消息
     * @param unread  是否未读
     */
    public void cacheRecord(ChatMessage message, boolean unread) {
        message.setUnread(unread);
        // 推送MQ
        messageQueue.syncSend(Topics.MESSAGE, JSONUtil.toJsonStr(message));
        // 目标列表添加发起者uid
        if (unread) {
            redisManager.setUnreadCount(message.getTarget(), message.getGuid(), 1);
        }
    }

    /**
     * 获取在线用户id列表
     */
    public List<String> getOnlineGuids() {
        return this.values()
                .stream()
                .filter(ChatUser::isOnline)
                .map(BaseUser::getGuid)
                .collect(Collectors.toList());
    }
}
